package dk.au.cs.bdsi.sms.representation;

import java.util.Arrays;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import dk.au.cs.bdsi.core.representation.ServiceUri;
import dk.au.cs.bdsi.sms.domain.MessageEncoding;
import dk.au.cs.bdsi.sms.domain.PhoneNumber;
import dk.au.cs.bdsi.sms.domain.Sms;
import dk.au.cs.bdsi.sms.domain.SmsStatus;

@XmlRootElement(name = "sms", namespace = Representation.NAMESPACE)
public class SmsRepresentation extends Representation {
	/**
	 * DAP {@link Link} which accepts a POST request with a supplied representation of
	 * the state of a {@link Sms} as identified by the supplied URI. Note that
	 * you can retry sending a Sms (which has status {@link SmsStatus#NotSent}) by
	 * updating it's status back to {@link SmsStatus#Pending}. 
	 */
	public final static String UpdateLink = Representation.RELATIONS_URI + "update";
	
	/**
	 * DAP {@link Link} which accepts a DELETE request to the given URI which identifies
	 * the specific resource to be deleted permanently.
	 */
	public final static String DeleteLink = Representation.RELATIONS_URI + "delete";
	
	@XmlElement(name = "from", namespace = Representation.NAMESPACE, required = true)
	private String from;
	
	@XmlElement(name = "to", namespace = Representation.NAMESPACE, required = true)
	private List<PhoneNumber> to;
	
	@XmlElement(name = "message", namespace = Representation.NAMESPACE, required = true)
	private String message;
	
	@XmlElement(name = "encoding", namespace = Representation.NAMESPACE, required = false)
	private MessageEncoding encoding;
	
	@XmlElement(name = "status", namespace = Representation.NAMESPACE, required = false)
	private SmsStatus status;
	
	SmsRepresentation() {/* For JAXB */}
	
	public SmsRepresentation(Sms sms, Link... links) {
		this.to = sms.getTo();
		this.from = sms.getFrom();
		this.message = sms.getMessage();
		this.encoding = sms.getEncoding();
		this.status = sms.getStatus();
		setLinks(Arrays.asList(links));
	}
	
	/**
	 * Create a new {@link SmsRepresentation} based on the domain object instance of a
	 * {@link Sms} as represented by the {@link ServiceUri}. I.e. it wraps a live domain
	 * object instance into a representation and annotates it with appropriate {@link Link}'s
	 * based on the current state of the give sms.
	 * 
	 * @param sms Live domain object
	 * @param smsUri URI of sms
	 * @return Representation of sms with appropriate links
	 */
	public static SmsRepresentation createResponseRepresentation(Sms sms, ServiceUri smsUri) {
		switch (sms.getStatus()) {
		case Pending:
			return new SmsRepresentation(sms, new Link(SELF_REL_VALUE, smsUri));
		case NotSent:
			return new SmsRepresentation(sms,
					new Link(UpdateLink, smsUri),
					new Link(DeleteLink, smsUri));
		case Sent:
			return new SmsRepresentation(sms, new Link(DeleteLink, smsUri));
		default:
				throw new RuntimeException("Unknown sms status");
		}
	}
	
	/**
	 * Unmarshall domain object from XML document.
	 * 
	 * @param xml XML document
	 * @return Domain object instance
	 */
	public static SmsRepresentation fromXml(String xml) {
		return unmarshal(xml, SmsRepresentation.class);
	}
	
	/**
	 * Marshall this representation into an XML document.
	 * 
	 * @return XML document
	 */
	public String toXml() {
		return marshall(this);
	}
	
	@Override
	public String toString() {
		return toXml();
	}
	
	/**
	 * Retrieve a live domain object {@link Sms}, based on the state of this
	 * representation.
	 * 
	 * @return Sms with state from this representation
	 */
	public Sms getSms() {
		return new Sms(from, to, message, encoding, status);
	}
	
	/**
	 * Retrieve a Self-link (if any) from this representation
	 * 
	 * @return Self-link or null
	 */
	public Link getSelfLink() {
		return getLinkByName(SELF_REL_VALUE);
	}
	
	/**
	 * Retrieve a Update-link (if any) from this representation
	 * 
	 * @return Update-link or null
	 */
	public Link getUpdateLink() {
		return getLinkByName(UpdateLink);
	}
	
	/**
	 * Retrieve a Delete-link (if any) from this representation
	 * 
	 * @return Delete-link or null
	 */
	public Link getDeleteLink() {
		return getLinkByName(DeleteLink);
	}
}
